package main;

import java.awt.Dimension;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import quadtree.AbstractNode.Cell;
import quadtree.AbstractNodeElement;
import quadtree.DoubleDimension;
import quadtree.DoublePoint;
import quadtree.NewQuadTree;
import quadtree.NewQuadTreeElement;
import quadtree.NewQuadTreeNode;
import quadtree.point.PointNode;
import quadtree.point.PointNodeElement;
import stateManager.StateManager;

public class SpriteTree implements Commons{
	
	private NewQuadTree<Sprite> spritesTree;	
	ExecutorService execSvc;
	boolean debug = false;
	ArrayList<Sprite> spriteTreeToDumbTree;
	private DrawTree drawTreeA;
	private DrawTree drawTreeB;
	public StateManager stateManager;
	
	public SpriteTree(){
		stateManager = StateManager.getInstance();
		execSvc = Executors.newFixedThreadPool(NUMBER_OF_RENDER_THREADS);
		spritesTree = new NewQuadTree<Sprite>(new DoublePoint(0, 0), new DoubleDimension(BOARD_WIDTH, BOARD_HEIGTH));
		spritesTree.predivide();
	}
	public NewQuadTreeNode<Sprite> getRootNode(){
		return spritesTree.getRootNode();		
	}
	
	public void insert(DoublePoint p, Sprite s){
		this.spritesTree.insert(p, s);
	}
	
	public void setDebug(boolean d){
		this.debug = d;
	}
	
	public void setSpriteTreeToDumbTree(ArrayList<Sprite> al){
		this.spriteTreeToDumbTree = al;
	}
	
	public void setDrawTreeA(DrawTree dt){
		this.drawTreeA = dt;
	}
	public void setDrawTreeB(DrawTree dt){
		this.drawTreeB = dt;
	}
	
	
	public Vector<NewQuadTreeElement<Sprite>> queryObject(DoublePoint coordinates, DoubleDimension size) {
		return this.spritesTree.queryObject(coordinates, size);
	}
	

	@SuppressWarnings("unchecked")
	public void thinkTreeNode(NewQuadTreeNode<Sprite> node, boolean cycleA, boolean cycleB) {
		/*
		 * MAKE EACH ELEMENT THINK, IF VISIBLE, KEEP IN THE VECTOR
		*/
		try{			
			Vector<NewQuadTreeElement<Sprite>> elements = (Vector<NewQuadTreeElement<Sprite>>) node
			.getElements();
			for (int i = 0; i < elements.size(); i++){
				//System.out.println("im thinking");
				NewQuadTreeElement<Sprite> element = elements.get(i);
				if((cycleA && !element.getElement().getWasThought())||(cycleB && element.getElement().getWasThought())) {
					execSvc.execute(element.getElement());
					//element.getElement().think();

					element.getElement().setThought(!element.getElement().getWasThought());
					if(element.getElement().isVisible() ){
						if(cycleA){
							drawTreeA.insert(element.getElement());
						}
						else{
							drawTreeB.insert(element.getElement());
						}
						//System.out.println("I INSERTED");
					}
				}
				
				// Check if the element has become uncollidable, and if so
				// add it to the holding ArrayList to be moved later
				if(element.getElement().isCollidable() == false){
					spriteTreeToDumbTree.add(element.getElement());
				}
				if(element.getElement().isAlive() == true 
						&& (element.getElement().getX() <= node.getStartCoordinates().x + node.getBounds().getWidth() || element.getElement().getX() >= spritesTree.getRootNode().bounds.width)
						&& (element.getElement().getY() <= node.getStartCoordinates().y + node.getBounds().getHeight() || element.getElement().getY() >= spritesTree.getRootNode().bounds.height)
						&& (element.getElement().getX() > node.getStartCoordinates().x || element.getElement().getX() <= 0)
						&& (element.getElement().getY() > node.getStartCoordinates().y || element.getElement().getY() <= 0)){
					//tmp_elements.add(element);
					/*if(element.getElement().isVisible() ) {//&& ((cycleA && element.getElement().getWasThought())||(cycleB && !element.getElement().getWasThought()))) {
						drawTree.insert(element.getElement());
					}*/
				}
				else if(element.getElement().isAlive()){
					node.removeElement(new DoublePoint(element.getElement().getX(),
							element.getElement().getY()),
							element.getElement());
					i--;
					spritesTree.insert(new DoublePoint(element.getElement().getX(),
							element.getElement().getY()), element.getElement());
					/*if(element.getElement().isVisible() ) {//&& ((cycleA && element.getElement().getWasThought())||(cycleB && !element.getElement().getWasThought()))) {
						drawTree.insert(element.getElement());
					}*/
				}
				else {
					node.removeElement(new DoublePoint(element.getElement().getX(),
							element.getElement().getY()),
							element.getElement());
					i--;
				}
				/*
				 * BEGIN COLLISION DETECTION AFTER EACH SPRITE MAKES ITS NEXT MOVE
				*/
				Sprite sprite0 = element.getElement();
				Sprite sprite1;
				Vector<NewQuadTreeElement<Sprite>> checkElements;
				checkElements = spritesTree.queryObject(new DoublePoint(sprite0.x, sprite0.y), 
						new DoubleDimension(sprite0.width, sprite0.height));
				if(checkElements != null) {
					for(int j = 0; j < checkElements.size(); j++) {
						sprite1 = checkElements.get(j).getElement();
						if(sprite1 != sprite0 && ((cycleA && !sprite1.getWasChecked())||(cycleB && sprite1.getWasChecked()))) {
							boolean yay = sprite0.doesCollide(sprite1);
							sprite0.setChecked(!sprite0.getWasChecked());
							if(debug) {
								System.out.println(elements.size() + " " + sprite0.getName() + " " + sprite1.getName() + " " + yay);
							}
						}
					}
				}
				//sprite0.setChecked(!sprite0.getWasChecked());
			}
			if(debug) {
				System.out.println(elements.size());
			}
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

	protected void thinkTree(NewQuadTreeNode<Sprite> node, boolean cycleA, boolean cycleB) {
		try{
			if (node.isDivided()) {
				thinkTree(node.TopLeft,cycleA, cycleB);
				thinkTree(node.TopRight,cycleA, cycleB);
				thinkTree(node.BottomLeft,cycleA, cycleB);
				thinkTree(node.BottomRight,cycleA, cycleB);
			}
			else {
				thinkTreeNode(node, cycleA, cycleB);
			}
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

	public DoublePoint getStartCoordinatesOfTree() {
		return spritesTree.getRootNode().startCoordinates;
	}
	
	public DoubleDimension getBoundsOfTree() {
		return spritesTree.getRootNode().bounds;
	}

}
